package com.czy.workflow.core.executor;

import com.czy.workflow.core.exception.FlowException;
import com.czy.workflow.core.exception.FlowExpressionException;
import com.czy.workflow.core.exception.FlowOperationTypeInvalidException;
import com.czy.workflow.core.model.*;
import com.czy.workflow.core.util.ExpressionUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class SimpleTaskExecutor extends AbstractTaskExecutor {



    @Override
    public boolean checkCurrentNode(TaskInfo taskInfo) {
        FlowNode currentNode = taskInfo.getNode();

        if(taskInfo.getNode() == null){
            return false;
        }

        //跳过开始节点检查
        if(OperationType.START.eq(currentNode.getOperationType())){
            return true;
        }

        //从缓存获取当前节点
        List<FlowNode> currentNodes = flowContext.getFlowService().getCurrentNodes(taskInfo.getFlowId(), taskInfo.getTaskId());
        for(FlowNode node : currentNodes){
            if(node.getId().equals(currentNode.getId()))    return true;
        }

        return false;
    }


    @Override
    public List<FlowNode> prepareNextNode(TaskInfo taskInfo, List<FlowBranch> nodeBranches) throws FlowException {
        List<FlowNode> nextNodes = new ArrayList<>();
        for(FlowBranch branch : nodeBranches){
            nextNodes.add(flowContext.getFlowService().getFlowNodeById(taskInfo.getFlowId(), branch.getTargetNodeId()));
        }

        //从缓存删除当前节点
        //将最新节点任务写入缓存
        flowContext.getFlowService().refreshCurrentNodes(taskInfo.getFlowId(), taskInfo.getTaskId(), nextNodes);

        //非用户操作的节点自动执行
        List<String> types = Arrays.asList(OperationType.START.getCode(), OperationType.AUTO.getCode(), OperationType.END.getCode());
        for(FlowNode node: nextNodes){
            if(types.contains(node.getOperationType())){
                this.setTaskInfo(copyTaskInfo(taskInfo, node));
                flowContext.getThreadPoolService().execute(this);
            }
        }
        return nextNodes;
    }



    @Override
    public boolean startTask(TaskInfo taskInfo)  throws FlowException {
        FlowMain flowMain = flowContext.getFlowService().getFlowMainById(taskInfo.getFlowId());
        taskInfo.setFlowCode(flowMain.getCode());
        this.setTaskInfo(taskInfo);
        List<FlowNode> nodes = flowContext.getFlowService().getStartNodes(taskInfo.getFlowId());
        for(FlowNode node : nodes){
            taskInfo.setNode(node);
            flowContext.getThreadPoolService().execute(this); //线程方式
        }

        flowContext.getTaskListenerNotify().start(taskInfo);

        return true;
    }

    @Override
    public boolean finishTask(TaskInfo taskInfo) {

        //清理临时缓存节点
        flowContext.getFlowService().clearCurrentNodes(taskInfo.getFlowId(), taskInfo.getTaskId());

        flowContext.getTaskListenerNotify().finished(taskInfo);

        logger.info(String.format("Task [%s] is finished", taskInfo.getTaskId()));

        return true;
    }


    @Override
    public boolean invokeExecutorComponent(TaskInfo taskInfo) {
        Object bean = flowContext.getExecutor(taskInfo.getFlowCode());
        Method method = flowContext.getMethod(taskInfo.getFlowCode(), taskInfo.getNode().getCode());
        if(bean != null && method != null){
            try {
                Boolean result = (Boolean) method.invoke(bean, taskInfo);
                if(result != null){
                    return result;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean executeNodeTask(TaskInfo taskInfo) throws FlowException {

        boolean result = true;

        FlowNode node = taskInfo.getNode();

        if(OperationType.START.eq(node.getOperationType())){
            List<FlowBranch> nodeBranches = autoTask(taskInfo);
            prepareNextNode(taskInfo, nodeBranches);
        }else if(OperationType.END.eq(node.getOperationType())){
            finishTask(taskInfo);
        }else if(OperationType.AUTO.eq(node.getOperationType())){
            List<FlowBranch> nodeBranches = autoTask(taskInfo);
            prepareNextNode(taskInfo, nodeBranches);
        }else if(OperationType.SELECTION.eq(node.getOperationType())){
            List<FlowBranch> nodeBranches = optionalTask(taskInfo);
            //用户操作或自定义任务可能导致当前任务失败
            if(nodeBranches != null && nodeBranches.size() > 0){
                prepareNextNode(taskInfo, nodeBranches);
            }
        }else{
            throw new FlowOperationTypeInvalidException(taskInfo);
        }

        return result;
    }


    public List<FlowBranch> autoTask(TaskInfo taskInfo) throws FlowExpressionException {

        List<FlowBranch> validBranches = new ArrayList<>();
        List<FlowBranch> branches = flowContext.getFlowService().getNodeBranches(taskInfo.getFlowId(), taskInfo.getNode().getId());

        for(FlowBranch branch : branches){
            if (ExpressionUtil.exeExpression(branch.getExpression(), taskInfo)){
                validBranches.add(branch);
            }
        }

        return validBranches;
    }


    public List<FlowBranch> optionalTask(TaskInfo taskInfo) {
        List<FlowBranch> validBranches = new ArrayList<>();
        FlowNode node = taskInfo.getNode();
        List<FlowBranch> branches = flowContext.getFlowService().getNodeBranches(taskInfo.getFlowId(), node.getId());

        //执行用户自定义任务
        if(!StringUtils.isEmpty(node.getExecutorCls())){
            try {
                TaskExecutor executor = (TaskExecutor) Class.forName(node.getExecutorCls()).newInstance();
                if(!executor.execute(taskInfo)){
                    return new ArrayList<>();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        for(FlowBranch branch : branches){
            if (branch.getOption().equals(taskInfo.getResult())){
                validBranches.add(branch);
            }
        }

        return validBranches;
    }



    @Override
    public boolean suspendTask(TaskInfo taskInfo) throws FlowException {
        return false;
    }

    @Override
    public boolean recoveryTask(TaskInfo taskInfo) throws FlowException {
        return false;
    }



    private TaskInfo copyTaskInfo(TaskInfo taskInfo, FlowNode nextNode){
        TaskInfo nextTaskInfo = new TaskInfo();
        BeanUtils.copyProperties(taskInfo, nextTaskInfo, "node");
        nextTaskInfo.setNode(nextNode);
        return nextTaskInfo;
    }
}
